/*
* #%L
* Native ARchive plugin for Maven
* %%
* Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.github.maven_nar;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
/**
* Sets up the javah configuration
*
* @author Mark Donszelmann
*/
public class Javah {
/**
* Javah command to run.
*/
@Parameter(defaultValue = "javah")
private String name = "javah";
/**
* Add boot class paths. By default none.
*/
@Parameter
private List/* <File> */bootClassPaths = new ArrayList();
/**
* Add class paths. By default the classDirectory directory is included and
* all dependent classes.
*/
@Parameter
private List/* <File> */classPaths = new ArrayList();
/**
* The target directory into which to generate the output.
*/
@Parameter(defaultValue = "${project.build.directory}/nar/javah-include", required = true)
private File jniDirectory;
/**
* The class directory to scan for class files with native interfaces.
*/
@Parameter(defaultValue = "${project.build.directory}/classes", required = true)
private File classDirectory;
/**
* The set of files/patterns to include Defaults to "**\/*.class"
*/
@Parameter
private Set includes = new HashSet();
/**
* A list of exclusion filters.
*/
@Parameter
private Set excludes = new HashSet();
/**
* A list of class names e.g. from java.sql.* that are also passed to javah.
*/
@Parameter
private Set extraClasses = new HashSet();
/**
* The granularity in milliseconds of the last modification date for testing
* whether a source needs recompilation
*/
@Parameter(defaultValue = "0", required = true)
private int staleMillis = 0;
/**
* The directory to store the timestampfile for the processed aid files.
* Defaults to jniDirectory.
*/
@Parameter
private File timestampDirectory;
/**
* The timestampfile for the processed class files. Defaults to name of javah.
*/
@Parameter
private File timestampFile;
private AbstractNarMojo mojo;
public Javah() {
}
public final void execute() throws MojoExecutionException, MojoFailureException {
getClassDirectory().mkdirs();
try {
final SourceInclusionScanner scanner = new StaleSourceScanner(this.staleMillis, getIncludes(), this.excludes);
if (getTimestampDirectory().exists()) {
scanner.addSourceMapping(new SingleTargetSourceMapping(".class", getTimestampFile().getPath()));
} else {
scanner.addSourceMapping(new SuffixMapping(".class", ".dummy"));
}
final Set classes = scanner.getIncludedSources(getClassDirectory(), getTimestampDirectory());
if (!classes.isEmpty()) {
final Set files = new HashSet();
for (final Object aClass : classes) {
final String file = ((File) aClass).getPath();
final JavaClass clazz = NarUtil.getBcelClass(file);
final Method[] method = clazz.getMethods();
for (final Method element : method) {
if (element.isNative()) {
files.add(clazz.getClassName());
}
}
}
if (!files.isEmpty()) {
getJniDirectory().mkdirs();
getTimestampDirectory().mkdirs();
final String javah = getJavah();
this.mojo.getLog().info("Running " + javah + " compiler on " + files.size() + " classes...");
final int result = NarUtil.runCommand(javah, generateArgs(files), null, null, this.mojo.getLog());
if (result != 0) {
throw new MojoFailureException(javah + " failed with exit code " + result + " 0x"
+ Integer.toHexString(result));
}
FileUtils.fileWrite(getTimestampDirectory() + "/" + getTimestampFile(), "");
}
}
} catch (final InclusionScanException e) {
throw new MojoExecutionException("JAVAH: Class scanning failed", e);
} catch (final IOException e) {
throw new MojoExecutionException("JAVAH: IO Exception", e);
} catch (final ClassFormatException e) {
throw new MojoExecutionException("JAVAH: Class could not be inspected", e);
}
}
private String[] generateArgs(final Set/* <String> */classes) throws MojoExecutionException {
final List args = new ArrayList();
if (!this.bootClassPaths.isEmpty()) {
args.add("-bootclasspath");
args.add(StringUtils.join(this.bootClassPaths.iterator(), File.pathSeparator));
}
args.add("-classpath");
args.add(StringUtils.join(getClassPaths().iterator(), File.pathSeparator));
args.add("-d");
args.add(getJniDirectory().getPath());
if (this.mojo.getLog().isDebugEnabled()) {
args.add("-verbose");
}
if (classes != null) {
for (final Object aClass : classes) {
args.add(aClass);
}
}
if (this.extraClasses != null) {
for (final Object extraClass : this.extraClasses) {
args.add(extraClass);
}
}
return (String[]) args.toArray(new String[args.size()]);
}
protected final File getClassDirectory() {
if (this.classDirectory == null) {
this.classDirectory = new File(this.mojo.getMavenProject().getBuild().getDirectory(), "classes");
}
return this.classDirectory;
}
protected final List getClassPaths() throws MojoExecutionException {
if (this.classPaths.isEmpty()) {
try {
this.classPaths.addAll(this.mojo.getMavenProject().getCompileClasspathElements());
} catch (final DependencyResolutionRequiredException e) {
throw new MojoExecutionException("JAVAH, cannot get classpath", e);
}
}
return this.classPaths;
}
protected final Set getIncludes() {
NarUtil.removeNulls(this.includes);
if (this.includes.isEmpty()) {
this.includes.add("**/*.class");
}
return this.includes;
}
private String getJavah() throws MojoExecutionException, MojoFailureException {
String javah = null;
// try toolchain
final Toolchain toolchain = getToolchain();
if (toolchain != null) {
javah = toolchain.findTool("javah");
}
// try java home
if (javah == null) {
final File javahFile = new File(this.mojo.getJavaHome(this.mojo.getAOL()), "bin");
javah = new File(javahFile, this.name).getAbsolutePath();
}
// forget it...
if (javah == null) {
throw new MojoExecutionException("NAR: Cannot find 'javah' in Toolchain or on JavaHome");
}
return javah;
}
protected final File getJniDirectory() {
if (this.jniDirectory == null) {
this.jniDirectory = new File(this.mojo.getMavenProject().getBuild().getDirectory(), "nar/javah-include");
}
return this.jniDirectory;
}
protected final File getTimestampDirectory() {
if (this.timestampDirectory == null) {
this.timestampDirectory = getJniDirectory();
}
return this.timestampDirectory;
}
protected final File getTimestampFile() {
if (this.timestampFile == null) {
this.timestampFile = new File(this.name);
}
return this.timestampFile;
}
// TODO remove the part with ToolchainManager lookup once we depend on
// 2.0.9 (have it as prerequisite). Define as regular component field then.
private Toolchain getToolchain() {
Toolchain toolChain = null;
final ToolchainManager toolchainManager = ((NarJavahMojo) this.mojo).getToolchainManager();
if (toolchainManager != null) {
toolChain = toolchainManager.getToolchainFromBuildContext("jdk", ((NarJavahMojo) this.mojo).getSession());
}
return toolChain;
}
public final void setAbstractCompileMojo(final AbstractNarMojo abstractNarMojo) {
this.mojo = abstractNarMojo;
}
}